﻿using System;
using System.Runtime.CompilerServices;
using CardGameCore.Battle;

[assembly: InternalsVisibleTo("DriverTest")]
namespace CardGameCore.Driver
{
    public class CoreDriver : ICoreDriver
    {
        public GameState State { get; internal set; } = GameState.Initializing;
        public IGameListener GameListener { get; set; }
        private bool Processing { get; set; }
        private bool PlayerAction { get; set; }
        private bool SelectedCard { get; set; }
        private object _actionParameters;

        private readonly IBattleInitialize _battleInitialize;
        private readonly IGamePrepare _gamePrepare;
        private readonly IRoundPrepare _roundPrepare;
        private readonly IPlayerSelectCard _playerSelectCard;
        private readonly IPlayerAction _playerAction;
        private readonly IBattleJudge _battleJudge;
        private readonly IEnemySelectCard _enemySelectCard;
        private readonly IEnemyAction _enemyAction;
        public CoreDriver(
            IBattleInitialize battleInitialize,
            IGamePrepare gamePrepare,
            IRoundPrepare roundPrepare,
            IPlayerSelectCard playerSelectCard,
            IPlayerAction playerAction,
            IBattleJudge battleJudge,
            IEnemySelectCard enemySelectCard,
            IEnemyAction enemyAction)
        {
            _battleInitialize = battleInitialize;
            _gamePrepare = gamePrepare;
            _roundPrepare = roundPrepare;
            _playerSelectCard = playerSelectCard;
            _playerAction = playerAction;
            _battleJudge = battleJudge;
            _enemySelectCard = enemySelectCard;
            _enemyAction = enemyAction;
        }

        public void Next()
        {
            if (Processing)
            {
                throw new InvalidOperationException(nameof(Processing));
            }

            Processing = true;
            OnNext();
            Processing = false;
        }

        private void OnNext()
        {
            switch (State)
            {
                case GameState.Initializing:
                    OnInitialize();
                    break;
                case GameState.GamePrepare:
                    OnGamePrepare();
                    break;
                case GameState.RoundStart:
                    OnRoundStart();
                    break;
                case GameState.PlayerSelectCard:
                    OnPlayerSelectCard();
                    break;
                case GameState.PlayerAction:
                    OnPlayerAction();
                    break;
                case GameState.EnemySelectCard:
                    OnEnemySelectCard();
                    break;
                case GameState.EnemyAction:
                    OnEnemyAction();
                    break;
                case GameState.Judge:
                    OnJudge();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void OnInitialize()
        {
            _battleInitialize.Initialize();
            GameListener?.OnGameInitialize();
            State = GameState.GamePrepare;
        }

        private void OnGamePrepare()
        {
            _gamePrepare.Process();
            GameListener?.OnGameReady();
            State = GameState.RoundStart;
        }

        private void OnRoundStart()
        {
            _roundPrepare.Process();
            GameListener?.OnRoundStart();
            State = GameState.Waiting;
        }

        public void SelectCard(SelectCardParameters parameters)
        {
            SelectedCard = true;
            ProcessUserAction(GameState.PlayerSelectCard);
        }

        private void OnPlayerSelectCard()
        {
            var result = _playerSelectCard.Process(UseActionParameters<SelectCardParameters>());
            GameListener?.OnPlayerSelectCard(result);
            result.Dispose();
            State = GameState.Waiting;
        }

        public void FinishRound()
        {
            ProcessUserAction(GameState.PlayerAction);
        }

        private void OnPlayerAction()
        {
            PlayerAction = true;
            var result = _playerAction.Process();
            GameListener?.OnPlayerAction(result);
            result.Dispose();
            State = GameState.Judge;
        }

        private void OnEnemySelectCard()
        {
            _enemySelectCard.Process();
            GameListener?.OnEnemySelectCard();
            State = GameState.EnemyAction;
        }

        private void OnEnemyAction()
        {
            var result = _enemyAction.Process();
            GameListener?.OnEnemyAction(result);
            result.Dispose();
            State = GameState.Judge;
        }

        private void OnJudge()
        {
            var result = _battleJudge.Process();
            GameListener?.OnJudge(result);
            if (result.Win)
            {
                State = GameState.Win;
            }
            else if (result.Fail)
            {
                State = GameState.Lose;
            }
            else
            {
                if (SelectedCard)
                {
                    State = GameState.Waiting;
                }
                else if (PlayerAction)
                {
                    State = GameState.EnemySelectCard;
                }
                else
                {
                    State = GameState.RoundStart;
                }
            }
            
            PlayerAction = false;
            SelectedCard = false;
            result.Dispose();
        }
        
        private void ProcessUserAction(GameState nextState)
        {
            if (State != GameState.Waiting)
            {
                throw new InvalidOperationException();
            }

            State = nextState;
        }
        
        private T UseActionParameters<T>() where T : class
        {
            T result = _actionParameters as T;
            _actionParameters = null;
            return result;
        }
    }
}